/***********************************************************************
 ***一个用于Linux串行通信的库
 ***作者：mario，2017.8.7
 ***版本：0.1
 ***特此授权任何获得本软件和相关文档文件（“软件”）副本的人免费处理本软件，
 ***包括但不限于使用，复制，修改，合并的权利，发布，分发，再授权和/或出售
 ***本软件的副本，并允许提供本软件的人员遵守以下条件：
 ***上述版权声明和本许可声明应包含在本软件的所有副本或主要部分。
 ***该软件“按原样”提供，不附带任何明示或暗示的保证，包括但不限于适销性，
 ***适用于特定用途和非侵权的担保。在任何情况下，作者或版权所有者均不对任何
 ***索赔，损害或其他责任负责，无论是否因与本软件或本软件的使用或其他交易相
 ***关的任何合同，侵权行为或其他方面的行为软件。
 ***********************************************************************/

#include "serial.h"

#ifndef CMSPAR
#define CMSPAR   010000000000 //注意这个是为了 MARK校验，SPACE校验
#endif

/************ 流控制 ********************/
#define FLOW_CONTROL_NONE         0
#define FLOW_CONTROL_HARDWARE     1
#define FLOW_CONTROL_SOFTWARE     2

/* 奇偶检验 */
#define PARITY_NONE  'N'
#define PARITY_EVEN  'E'
#define PARITY_ODD   'O'
#define PARITY_SPACE 'S'
#define PARITY_MARK  'M'

int speed_arr[]={B115200, B38400, B19200, B9600, B4800, B2400, B1200, B300};
int name_arr[]={115200, 38400, 19200, 9600, 4800, 2400, 1200, 300};

/***********************************************************************
***函数功能：打开串口
***输入参数：*dev 设备名称
***返回值  ：fd 或者 FALSE
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int OpenDev(char *dev)
{
  int fd=open(dev,O_RDWR|O_NOCTTY|O_NDELAY);
  //int fd=open(dev,O_RDWR|O_NOCTTY);
  //int fd=open(dev,O_RDWR|O_NOCTTY|O_NONBLOCK);
  if(fd<0)
  {
	printf("Can't Open Serial Port!\n");
        return FALSE;
  }
  else
	return fd;
}

/***********************************************************************
***函数功能：设置波特率
***输入参数：fd:设备返回值，speed:波特率值，newtio:串口结构控制位设置值
***返回值  ：TRUE 或者 FALSE
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int Set_speed(int fd,int speed,struct termios *newtio)
{
  int i;
  int status;
  struct termios *Opt=newtio;
  tcgetattr(fd,Opt);
  for(i=0;i<sizeof(speed_arr)/sizeof(int);i++)
  {
	if(speed==name_arr[i])
	{
	  tcflush(fd,TCIOFLUSH);
	  cfsetispeed(Opt,speed_arr[i]);
	  cfsetospeed(Opt,speed_arr[i]);
	  printf("%d\n",i);
	  status=tcsetattr(fd,TCSANOW,Opt);
	  if(status !=0)
	  {
		printf("tcsetattr failed!\n");
		return FALSE;
	  }
	  tcflush(fd,TCIOFLUSH);
	}
  }
  return TRUE;
}

/***********************************************************************
***函数功能：设置fd的数据位、停止位、奇偶检验位
***输入参数：fd:设备返回值,databits:数据位,stopbits:停止位,parity:奇偶检验位,
            flow_ctrl:数据流控制,cmin:接收最小字节,times:超时时间（0.1s)
***返回值  ：TRUE 或者 FALSE
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int Set_attr(int fd,int databits,int stopbits,char parity,int flow_ctrl,int cmin,int times)
{
   struct termios options;
   if(tcgetattr(fd,&options)!=0)
   {
	printf("Setup Serial 1 error!\n");
	return FALSE;
   }
   //对options的起始地址开始的termios结构体内存置零
   bzero(&options,sizeof(options));
   options.c_cflag &=~CSIZE;
   
   //设置数据流控制
   switch(flow_ctrl)
   {
	case FLOW_CONTROL_NONE :
		options.c_cflag &= ~CRTSCTS;
		break;
	case FLOW_CONTROL_HARDWARE :
		options.c_cflag |= CRTSCTS;
		break;
	case FLOW_CONTROL_SOFTWARE :
		options.c_cflag |= IXON | IXOFF | IXANY;
		break;
	default:
		printf("Unsupported flow_ctrl!\n");
		return FALSE;
		
   }
   //选择数据位
   switch(databits)
   {
	case 5:
		options.c_cflag |=CS5;
		break;	
	case 6:
		options.c_cflag |=CS6;
		break;	
	case 7:
		options.c_cflag |=CS7;
		break;
	case 8:
		options.c_cflag |=CS8;
		break;
	default:
		printf("Unsupported data size!\n");
		return FALSE;
   }
   //选择奇偶校验
   switch(parity)
   {
	case PARITY_NONE://无校验 N
		options.c_cflag &=~(PARENB);
		options.c_cflag &=~(PARODD);
		//options.c_cflag &=~(INPCK);
		options.c_cflag |=INPCK;
		//printf("df\n");
		break;
	//case 'o'://奇校验
	case PARITY_ODD:
		options.c_cflag |=(PARODD|PARENB);
		options.c_cflag |=INPCK;
		break;
	//case 'e'://偶校验
	case PARITY_EVEN:
		options.c_cflag |=PARENB;
		options.c_cflag &=~(PARODD);
		options.c_cflag |=INPCK;
		break;
	//case 's':
	case PARITY_SPACE:
		options.c_cflag |= PARENB | CMSPAR;
		options.c_cflag &= ~PARODD;
		break;
        //case 'm':
	case PARITY_MARK:
		options.c_cflag |= PARENB | CMSPAR | PARODD;
		break;
	default:
		printf("Unsupported parity!\n");
		return FALSE;
   }
   switch(stopbits)
   {
	case 1:
		options.c_cflag &=~(CSTOPB);
		break;
	case 2:
		options.c_cflag |=CSTOPB;
		break;
	default:
		printf("Unsupported stop bits!\n");
		return FALSE;
   }

   if(parity !='N')
   {
	//修改控制模式，保证程序不会占用串口
	options.c_cflag |=CLOCAL;
	//修改控制模式，使得能够从串口中读取输入数据
	options.c_cflag |=CREAD;
	options.c_cflag &=~(ICANON|ECHO|ECHOE);
	options.c_cflag &=~(IXON|IXOFF|IXANY);
	options.c_cflag &=~(ICANON|ECHO|ECHOE|ISIG);
	options.c_cflag &=~OPOST;
   }

   //有字符处理或经过TIME个0.1秒后返回
   options.c_cc[VTIME]=times;
   options.c_cc[VMIN]=cmin;//0 最小接收字符
   //如果发生数据溢出，接收数据，但是不再读取
   tcflush(fd,TCIFLUSH);
   //激活配置（将修改后的termios数据设置到串口中）
   if(tcsetattr(fd,TCSANOW,&options)!=0)
   {
	printf("Setup Serial error!\n");
	return FALSE;
   }
   return TRUE;
}

/***********************************************************************
***函数功能：写数据函数
***输入参数：fd:设备文件，buf:发送缓存区，data_len:数据长度
***返回值  ：write_count:发送字节数
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int Write_Data(int fd, void *buf, size_t data_len)
{
   int m_fd=fd;
   int write_count=0;
   ssize_t nwrite=0;
   
   if(m_fd<0)//检查设备文件情况
   {
	printf("Please Open The Device File!\n");
	return -1;//设备出错返回-1,做判断
   }
   while(data_len>0)
   {
	nwrite = write(fd, (char*)buf + write_count, data_len);	
	//nwrite=write(fd,buf,data_len);
	if(nwrite<1)
	{
	  printf("Write Data Fail!\n");
	  break;
	}
	write_count +=nwrite;
	data_len -=nwrite;
   }
   tcflush(fd,TCIOFLUSH);// 清除所有正在发生的I/O数据。
   //tcflush(fd, TCOFLUSH);// 清除正写入的数据，且不会发送至终端。
   return write_count;
}

/***********************************************************************
***函数功能：读取数据函数
***输入参数：fd:设备文件,data_len:数据长度,timeout_millisec: select超时时间
***返回值  ：buff:接收缓存区
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
int Read_Data(int fd,un_char *buff,size_t data_len,unsigned int timeout_millisec)
{
   int nread=0;
   int nselect;
   int fd_max;
   int m_fd=fd; 
   un_char inbuff[data_len];
   un_char readbuff[data_len];

   memset(readbuff,0,sizeof(readbuff));

   struct timeval timeout;//select阻塞超时定时器
   if(timeout_millisec == 0)//不等待
   {     
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
   }
   else//等待时间毫秒
   {
	timeout.tv_sec = timeout_millisec / 1000;
        timeout.tv_usec = (timeout_millisec % 1000) * 1000;
   }
   
   if(m_fd<0)//检查设备文件情况
   {
	printf("Please Open The Device File!\n");
	return -1;//设备出错返回-1,做判断
   }

   fd_set readfds;
   FD_ZERO(&readfds);//清空集合
   FD_SET(m_fd,&readfds);//加入集合 集合中删除FD_CLR
   fd_max=m_fd+1;
   
   do
   {
      do
      {
   	nselect=select(fd_max,&readfds,NULL,NULL,&timeout); 
      }while(-1==nselect && errno==EINTR);
   
      if(-1==nselect)
	printf("select(2)\n");
      if(nselect==0)
        m_fd=-1;
      if(m_fd>=0 && FD_ISSET(m_fd,&readfds))
      {
	nselect=read(m_fd,readbuff,data_len-nread);
	nread+=nselect;
	if(nselect==-1)
	{
		m_fd=-1;
	}
	if(nselect>0)
	{
		readbuff[nselect+1]='\0';
		//strcat(inbuff,readbuff);//只能拷贝char型
		memcpy(inbuff,readbuff,data_len);
		memset(readbuff,0x00,data_len);
	}
	else
	{
		m_fd=-1;
	}
      }
   }while(m_fd>=0);
   memcpy(buff,inbuff,nread);//拷贝输出
/********************************************************
   int j=0;//测试用
   while(inbuff[j]!='\0')
   {
	printf("the readable data is 0x%x\n",inbuff[j]);
	j++;
   }
*********************************************************/
   return nread;
}

/***********************************************************************
***函数功能：接收数据解析
***输入参数：buff:接收缓存区,data_len:数据长度
***返回值  ：a:加速度(x,y,z),w:角速度(x,y,z),h:磁场(x,y,z),Angle:角度(x,y,z)
***         port:端口控制，TPA[0]:温度,TPA[1]:气压，TPA[2]:高度
***修改时间：2017-8-8 V0.1
***作者    ：mario
************************************************************************/
void Data_Processing(un_char *buff,size_t data_len,double a[4],double w[4],double h[4],double Angle[4],double Port[4],double TPA[3])
{
   char data[data_len],updata[11];
   ssize_t usRxLength=data_len;
   int i=0,t=0;
   memset(data,0,data_len);
   memset(updata,0,11);
   memcpy(data,buff,data_len);

   while(usRxLength>=11)
    {
        if(!((data[0]==0x55)&((data[1]&0x50)==0x50)))
        {
           for (i = 1; i < usRxLength; i++) data[i - 1] = data[i];
           usRxLength--;
           continue;
        }
        if (((data[0]+data[1]+data[2]+data[3]+data[4]+data[5]+data[6]+data[7]+data[8]+data[9])&0xff)==data[10])
        {
           memcpy(updata,data,11);
           switch (updata[1])
    	   {
		case 0x50:
            		break;
        	case 0x51:
            		//0x55 0x51 加速度
            		a[0]=(((updata[3]<<8)&0xff00)|(updata[2]&0x00ff))/32768.0*16;//g,9.8m/s^2
            		a[1]=(((updata[5]<<8)&0xff00)|(updata[4]&0x00ff))/32768.0*16;//g,9.8m/s^2
            		a[2]=(((updata[7]<<8)&0xff00)|(updata[6]&0x00ff))/32768.0*16;//g,9.8m/s^2
            		a[3]=(((updata[9]<<8)&0xff00)|(updata[8]&0x00ff))/100.0;//  ℃
			TPA[0]=a[3]; //0x55 0x51 0x52 0x53 0x54 温度℃
            		for(t=0;t<3;t++)
            		{
                	   if(a[t]>=16)
                           {
                              a[t]=a[t]-32;
                           }
                           else
                           {
                              a[t]=a[t]+0;
                           }
                        }
            		break;
        	case 0x52:
            		//0x55 0x52 角速度
            		w[0]=(((updata[3]<<8)&0xff00)|(updata[2]&0x00ff))/32768.0*2000;
            		w[1]=(((updata[5]<<8)&0xff00)|(updata[4]&0x00ff))/32768.0*2000;
            		w[2]=(((updata[7]<<8)&0xff00)|(updata[6]&0x00ff))/32768.0*2000;
            		w[3]=(((updata[9]<<8)&0xff00)|(updata[8]&0x00ff));
            		for(t=0;t<3;t++)
            		{
                	   if(w[t]>=2000)
                           {
                   	      w[t]=w[t]-4000;
                	   }
                	   else
                       	   {
                              w[t]=w[t]+0;
                           }
            		}
            		//Temperature=w[3]/100.0; //0x55 0x51 0x52 0x53 0x54 温度℃
            		break;
        	case 0x53:
            		//0x55 0x53 角度
            		Angle[0]=(((updata[3]<<8)&0xff00)|(updata[2]&0x00ff))/32768.0*180;
            		Angle[1]=(((updata[5]<<8)&0xff00)|(updata[4]&0x00ff))/32768.0*180;
            		Angle[2]=(((updata[7]<<8)&0xff00)|(updata[6]&0x00ff))/32768.0*180;
            		Angle[3]=(((updata[9]<<8)&0xff00)|(updata[8]&0x00ff));
            		for(t=0;t<3;t++)
            		{
                	   if(Angle[t]>=180)
                	   {
                    	      Angle[t]=Angle[t]-360;
                	   }
                	   else
                	   {
                    	      Angle[t]=Angle[t]+0;
                	   }
            		}
            		//Temperature=Angle[3]/100.0; //0x55 0x51 0x52 0x53 0x54 温度℃
            		break;
        	case 0x54:
            		//0x55 0x54 磁场
            		h[0]=(((updata[3]<<8)&0xff00)|(updata[2]&0x00ff));
            		h[1]=(((updata[5]<<8)&0xff00)|(updata[4]&0x00ff));
            		h[2]=(((updata[7]<<8)&0xff00)|(updata[6]&0x00ff));
            		h[3]=(((updata[9]<<8)&0xff00)|(updata[8]&0x00ff));
            		for(t=0;t<3;t++)
            		{
                	   if(h[t]>=32768)
                	   {
                    	      h[t]=h[t]-32768;
                	   }
                	   else
                	   {
                    	      h[t]=h[t]+0;
                	   }
            		}
            		//Temperature=h[3]/100.0; //0x55 0x51 0x52 0x53 0x54 温度℃	    
            		break;
        	case 0x55:
            		//0x55 0x55  端口
            		Port[0]=(((updata[3]<<8)&0xff00)|(updata[2]&0x00ff));
            		Port[1]=(((updata[5]<<8)&0xff00)|(updata[4]&0x00ff));
            		Port[2]=(((updata[7]<<8)&0xff00)|(updata[6]&0x00ff));
            		Port[3]=(((updata[9]<<8)&0xff00)|(updata[8]&0x00ff));
            		break;
        	case 0x56:
            		//0x55 0x56  气压  高度
            		TPA[1]=(((updata[5]<<24)&0xff000000)|((updata[4]<<16)&0x00ff0000)|((updata[3]<<8)&0x0000ff00)|(updata[2]&0x000000ff))/1+1;
            		TPA[2]=(((updata[9]<<24)&0xff000000)|((updata[8]<<16)&0x00ff0000)|((updata[7]<<8)&0x0000ff00)|(updata[6]&0x000000ff))/100.0;
            		if(TPA[1]>=2147483648)
                	   TPA[1]=TPA[1]-4294967296;//0xffffffff+1
            		else
                	   TPA[1]=TPA[1]+0;
            		if(TPA[2]>=21474836.48)
                	   TPA[2]=(TPA[2]-42949672.96);//0xffffffff+1
            		else
                	   TPA[2]=TPA[2]+0;
            		break;
        	case 0x57:
            		//经纬度
            		break;
        	case 0x58:
            		//地速
            		break;
        	case 0x59:
            		//四元素
            		break;
        	case 0x5A:
            		//卫星定位精度
            		break;
        	default:
            		break;
           }
        }
        for (i = 11; i < usRxLength; i++) data[i - 11] = data[i];
        usRxLength -= 11;
    }
   //return 1;
}

